#include "OptionalType.h"

#include "GenericInfo.h"
#include "PlaceholderType.h"
#include "NativeFunction.h"
#include "FunctionList.h"
#include "Containers/ReflectableOptional.h"
#include "TypeInstance.h"
#include "GarbageCollectable.h"

CString CTOptionalType::PlaceholderName=U"T";

CTOptionalType::CTOptionalType()
    :CType(
        STypeID(CType::GetEngineCoreNamespace()+U"::TOptional",true)
        ,ETypeFlags::Generic | ETypeFlags::Optional
        )
{
    GenericInfo=new CGenericInfo();

    CPlaceholderType* OptionalPlaceholderType = 
        new CPlaceholderType(
            STypeID(GetFullName()+U"::"+PlaceholderName,true)
            ,ETypeFlags::None
            ,0
            ,this);
    GenericInfo->PlaceholderTypes.Add(OptionalPlaceholderType);

    //add functions
    Functions=new CFunctionList(this);

    //add no parameter constructor
    {
        CNativeFunction* Constructor = new CNativeFunction(
            Functions
            ,CFunction::GetConstructorName()
            , CGetType<void>()()
            , {}
            ,{}
            );
    }

    //add constructor that construct from nullptr
    {
        CNativeFunction* Constructor = new CNativeFunction(
            Functions
            ,CFunction::GetConstructorName()
            , CGetType<void>()()
            , {CGetType<std::nullptr_t>()()}
            ,{}
            );
    }

    //add Constructor that construct from value
    {
        CNativeFunction* Constructor = new CNativeFunction(
            Functions
            ,CFunction::GetConstructorName()
            , CGetType<void>()()
            , {OptionalPlaceholderType}
            ,{}
            );
    }

    //add HasValue function
    {
        CNativeFunction* HasValueFunction = new CNativeFunction(
            Functions
            ,U"HasValue"
            , CGetType<bool>()()
            , {}
            ,{}
            );
    }

    //add GetValue function
    {
        CNativeFunction* GetValueFunction = new CNativeFunction(
            Functions
            ,U"GetValue"
            , OptionalPlaceholderType
            , {}
            ,{}
            );
    }

    //add SetValue function
    {
        CNativeFunction* SetValueFunction = new CNativeFunction(
            Functions
            ,U"SetValue"
            , CGetType<void>()()
            , {OptionalPlaceholderType}
            ,{}
            );
    }

    //add Reset Function
    {
        CNativeFunction* ResetFunction = new CNativeFunction(
            Functions
            ,U"Reset"
            , CGetType<void>()()
            , {}
            ,{}
            );
    }
}

CTOptionalType *CTOptionalType::GetStaticType()
{
    static CTOptionalType* StaticType=new CTOptionalType();
    return StaticType;
}

CString CTOptionalType::CreateTypeName(const CString &ElementType)
{
    return STypeID::CreateTemplateTypeName(
        GetStaticType()->GetFullName()
        ,{ElementType}
        ,{PlaceholderName}
        );
}

STypeID CTOptionalType::CreateID(const SDecoratedType &ElementType)
{
    return STypeID(
        GetStaticType()->ID
        ,{ElementType.BasicType->ID}
        ,{PlaceholderName}
        );
}

uint8_t RegisterTypesCaller=(CTOptionalType::GetStaticType(),0);



class COptionalInstance: public ITypeInstance
{
protected:
    CTOptionalTypeGenerialized * Type;
public:
    COptionalInstance(CTOptionalTypeGenerialized * InType)
        :Type(InType)
    {
    }

    virtual void InitInstance(void* InMemory) override
    {
        new (InMemory) CReflectableOptional();
    }

    uint32_t GetSize() override
    {
        return sizeof(CReflectableOptional);
    }

    virtual void EraseInstance(void * Instance) override
    {
        reinterpret_cast<CReflectableOptional*>(Instance)->EraseValueWithType(Type->GetInnerType());
    }

    void CloneInstance(const void * Instance, void * Memory ) override
    {
        CReflectableOptional * Optional= new (Memory) CReflectableOptional();
        reinterpret_cast<const CReflectableOptional*>(Instance)->CloneWithType(Type->GetInnerType(),Optional);
    }

    void* CreateInstance() override
    {
        return new CReflectableOptional();
    }

    void ResetAllocated(void* InMemory) override
    {
        reinterpret_cast<CReflectableOptional*>(InMemory)->EraseValueWithType(Type->GetInnerType());
    }

    uint64_t GetInstanceHash(const void * InInstance) const
    {
        if(!reinterpret_cast<const CReflectableOptional*>(InInstance)->HasValue())
        {
            return 0;
        }

        return Type->GetInnerType()->GetInstanceHash(
            reinterpret_cast<const CReflectableOptional*>(InInstance)->GetValue()
        );
    }

    bool IsInstanceEqual(const void * A, const void * B) const
    {
        return Type->GetInnerType()->IsInstanceEqual(
            reinterpret_cast<const CReflectableOptional*>(A)->GetValue()
            ,reinterpret_cast<const CReflectableOptional*>(B)->GetValue()
        );
    }

    CString ToDebugString(const void * InInstance) const override
    {
        const CReflectableOptional* Optional
            =reinterpret_cast<const CReflectableOptional*>(InInstance);
        if(!Optional->HasValue())
        {
            return U"NullOptional";
        }

        return Type->GetInnerType()->ToDebugString(
            Optional ->GetValue()
        );
    }

    bool IsForceConvertableTo(CType * Other) override
    {
        if(!Other->IsOptionalType())
        {
            return false;
        }
        auto OtherOptional=reinterpret_cast<CTOptionalTypeGenerialized*>(Other);
        return Type
            ->GetInnerType()
            ->IsForceConvertableTo(OtherOptional->InnerType);
    }
};


class COptionalGC:public IGarbageCollectable
{
protected:
    CTOptionalTypeGenerialized* Type;
public:
    COptionalGC(CTOptionalTypeGenerialized* InType)
        :Type(InType)
    {}

    virtual void CollectReferencedObjects(
        void* InInstance,
        TForwardForwardLinkedList<RObject*>& OutReferencedObjects
    ) override
    {
        auto Optional=reinterpret_cast<CReflectableOptional*>(InInstance);
        if(Optional->HasValue())
        {
            Type->InnerType->GarbageCollectable
                ->CollectReferencedObjects(Optional->GetValue(),OutReferencedObjects);
        }
    }
};

CTOptionalTypeGenerialized::CTOptionalTypeGenerialized(
    const SDecoratedType& InElementType
    ,ETypeFlags InFlags
    )
    :CType(
        CTOptionalType::CreateID(InElementType)
        ,InFlags | ETypeFlags::Optional | ETypeFlags::Generalized
         |(InElementType.BasicType->CanUseInScript()? ETypeFlags::None : ETypeFlags::CantUseInScript)
        )
{
    this->InnerType=InElementType.BasicType;
    Instancable=new COptionalInstance(this);

    if(InnerType->GarbageCollectable)
    {
        GarbageCollectable=new COptionalGC(this);
    }

    GeneralizedInfo=new CGeneralizedInfo();
    GeneralizedInfo->TemplateType=CTOptionalType::GetStaticType();
    GeneralizedInfo->TypeArguments.Add(InElementType.BasicType);

    GeneralizedInfo->TemplateType->GenericInfo->GeneralizedTypes.Add(this);
    reinterpret_cast<CGenericInfo*>(GeneralizedInfo->TemplateType->GenericInfo)->GenericMap.Add(TVector<CType*>({InElementType.BasicType}),this);

    //add Functions
    Functions= new CFunctionList(this);
    
    //add empty constructor 
    {
        CNativeFunction* Constructor = new CNativeFunction(
            Functions
            ,CFunction::GetConstructorName()
            , CGetType<void>()()
            , {}
            ,{}
            );

        Constructor->SetCppBody([](const TVector<CAny>& Args)->CAny
        {
            //do nothing
            return CAny();
        });

    }

    //add constructor that construct from nullptr
    {
        CNativeFunction* Constructor = new CNativeFunction(
            Functions
            ,CFunction::GetConstructorName()
            , CGetType<void>()()
            , {CGetType<std::nullptr_t>()()}
            ,{}
            );

        Constructor->SetCppBody([](const TVector<CAny>& Args)->CAny
        {
            //do nothing
            return CAny();
        });

    }

    //add Constructor that construct from value
    {
        CNativeFunction* Constructor = new CNativeFunction(
            Functions
            ,CFunction::GetConstructorName()
            , CGetType<void>()()
            , {InnerType}
            ,{}
            );

        Constructor->SetCppBody([this](const TVector<CAny>& Args)->CAny
        {
            CReflectableOptional* Optional=
                *reinterpret_cast<CReflectableOptional**>(Args[0].GetPointer());
            
            assert(Args[1].GetType()->IsForceConvertableTo(this->InnerType));

            Optional->SetValueWithType(
                this->InnerType
                ,Args[1].GetPointer());
            return CAny();
        });
    }

    //add HasValue function
    {

        CNativeFunction* HasValueFunction = new CNativeFunction(
            Functions
            ,U"HasValue"
            , CGetType<bool>()()
            , {}
            ,{}
            );

        HasValueFunction->SetCppBody([](const TVector<CAny>& Args)->CAny
        {
            CReflectableOptional* Optional=
                *reinterpret_cast<CReflectableOptional**>(Args[0].GetPointer());
            
            return Optional->HasValue();
        });
    }

    //add GetValue function
    {
        CNativeFunction* GetValueFunction = new CNativeFunction(
            Functions
            ,U"GetValue"
            , InnerType
            , {}
            ,{}
            );

        GetValueFunction->SetCppBody([this](const TVector<CAny>& Args)->CAny
        {
            CReflectableOptional* Optional=
                *reinterpret_cast<CReflectableOptional**>(Args[0].GetPointer());

            assert(Optional->HasValue());

            return CAny(this->InnerType,Optional->GetValue());
        });
    }

    //add SetValue function
    {
        CNativeFunction* SetValueFunction = new CNativeFunction(
            Functions
            ,U"SetValue"
            , CGetType<void>()()
            , {InnerType}
            ,{}
            );

        SetValueFunction->SetCppBody([this](const TVector<CAny>& Args)->CAny
        {
            CReflectableOptional* Optional=
                *reinterpret_cast<CReflectableOptional**>(Args[0].GetPointer());
            
            assert(Args[1].GetType()->IsForceConvertableTo(this->InnerType));

            Optional->SetValueWithType(
                this->InnerType
                ,Args[1].GetPointer());
            return CAny();
        });
    }

    //add Reset Function
    {
        CNativeFunction* ResetFunction = new CNativeFunction(
            Functions
            ,U"Reset"
            , CGetType<void>()()
            , {}
            ,{}
            );

        ResetFunction->SetCppBody([this](const TVector<CAny>& Args)->CAny
        {
            CReflectableOptional* Optional=
                *reinterpret_cast<CReflectableOptional**>(Args[0].GetPointer());
            
            Optional->EraseValueWithType(this->InnerType);
            return CAny();
        });
    }
}

